<script>
import { deepClone } from "../utils/index";
import render from "../render/render.js";

const ruleTrigger = {
  "el-input": "blur",
  "el-input-number": "blur",
  "el-select": "change",
  "el-radio-group": "change",
  "el-checkbox-group": "change",
  "el-cascader": "change",
  "el-time-picker": "change",
  "el-date-picker": "change",
  "el-rate": "change"
};
const selectVerification = function(rule, value, callback) {
  if (!value) {
    callback(new Error(rule.message));
  }
  callback();
};

const layouts = {
  colFormItem(h, scheme, model) {
    const config = scheme.__config__;
    const listeners = buildListeners.call(this, scheme);

    let labelWidth = config.labelWidth ? `${config.labelWidth}px` : null;
    if (config.showLabel === false) labelWidth = "0";
    let display = scheme.__config__.hidden ? "none" : "block";
    // console.log(display)
    return (
      <el-col
        span={config.span}
        style={"display:" + display}
        class={scheme.__config__.jsClass}
      >
        <el-form-item
          label-width={labelWidth}
          prop={scheme.__vModel__}
          label={config.showLabel ? config.label : ""}
        >
          <render conf={scheme} {...{ on: listeners }} />
        </el-form-item>
      </el-col>
    );
  },
  rowFormItem(h, scheme, model) {
    let child = renderChildren.apply(this, arguments, model);
    if (scheme.type === "flex") {
      child = (
        <el-row
          type={scheme.type}
          justify={scheme.justify}
          align={scheme.align}
          class={scheme.__config__.jsClass}
        >
          {child}
        </el-row>
      );
    }
    return (
      <el-col span={scheme.span} class={scheme.__config__.jsClass}>
        <el-row gutter={scheme.gutter} class="mobile-row">
          {child}
        </el-row>
      </el-col>
    );
  },
  szFormItem(h, scheme, model) {
    let child = renderChildren.apply(this, arguments, model);
    if (scheme.type === "flex") {
      child = (
        <el-row
          type={scheme.type}
          justify={scheme.justify}
          align={scheme.align}
          class={scheme.__config__.jsClass}
        >
          {child}
        </el-row>
      );
    }
    return (
      <sz-form-item
        headerTitle={scheme.headerTitle}
        span={scheme.__config__.span}
        gutter={scheme.__config__.gutter}
        config={scheme.__config__}
        class={scheme.__config__.jsClass}
      >
        {child}
      </sz-form-item>
    );
  },
  szForm(h, scheme, model) {
    let child = renderChildren.apply(this, arguments, model);

    if (scheme.type === "flex") {
      child = (
        <el-row
          type={scheme.type}
          justify={scheme.justify}
          align={scheme.align}
          class={scheme.__config__.jsClass}
        >
          {child}
        </el-row>
      );
    }
    return (
      <sz-form
        btnName={scheme.btnName}
        limitItem={scheme.limitItem}
        config={scheme.__config__}
        class={scheme.__config__.jsClass}
      >
        <el-col span={scheme.span}>
          <el-row gutter={scheme.gutter} class="mobile-row">
            {child}
          </el-row>
        </el-col>
      </sz-form>
    );
  },

  tableFormItem(h, scheme, model) {
    let child = renderChildren.apply(this, arguments, model);
    return (
      <el-col span={scheme.__config__.span} class={scheme.__config__.jsClass}>
        <el-row gutter={scheme.__config__.gutter}>
          <table style={scheme.__config__.style} class="table_form">
            {child}
          </table>
        </el-row>
      </el-col>
    );
  },
  trFormItem(h, scheme, model) {
    let child = renderChildren.apply(this, arguments, model);

    return (
      <tr style={scheme.__config__.style} class={scheme.__config__.jsClass}>
        {child}
      </tr>
    );
  },
  labelItem(h, scheme, model) {
    return (
      <div style={scheme.__config__.style} class={scheme.__config__.jsClass}>
        <span>{scheme.__config__.text}</span>
      </div>
    );
  },
  labelInput(h, scheme, model) {
    // console.log(model);
    return (
      <div class={scheme.__config__.jsClass}>
        <label-input
          items={scheme.__config__.items}
          align={scheme.__config__.align}
          form-model={model}
        ></label-input>
      </div>
    );
  },
  cityRuralSelect(h, scheme, model) {
    // console.log(scheme,'scheme');
    return (
      <city-rural-select selectModel={scheme.selectModel}></city-rural-select>
    );
  },
  tagInput(h, scheme, model) {
    // console.log(h, scheme,model)
    // return (
    //   <div class={scheme.__config__.jsClass}>
    //     <tag-input
    //       tagType={scheme.__config__.tagType}
    //       align={scheme.__config__.align}
    //       format={scheme.__config__.format}
    //       showSignButton={scheme.__config__.showSignButton}
    //       showAcceptButton={scheme.__config__.showAcceptButton}
    //       showRejectButton={scheme.__config__.showRejectButton}
    //       showCancelButton={scheme.__config__.showCancelButton}
    //       isMerge={scheme.__config__.isMerge}
    //       addDate={scheme.__config__.addDate}
    //       signAlign={scheme.__config__.signAlign}
    //       readonly={scheme.readonly}
    //       required={scheme.__config__.required}
    //       placeholder={scheme.placeholder}
    //       fieldName={scheme.__config__.__vModel__}
    //       defaultValue={scheme.__config__.defaultValue}
    //     ></tag-input>
    //   </div>
    // );
    scheme.__config__.layout = "colFormItem";
    scheme.__config__.tag = "tag-input";
    if (!scheme.__config__.labelWidth) {
      scheme.__config__.labelWidth = 1;
    }
    scheme.__vModel__ = scheme.__vModel__ || scheme.__config__.__vModel__;
    scheme.__config__.key = scheme.__vModel__;
    return layouts.colFormItem(h, scheme, model);
  },
  szQuickReply(h, scheme, model) {
    return (
      <div class={scheme.__config__.jsClass}>
        <sz-quick-reply
          align={scheme.__config__.align}
          format={scheme.__config__.format}
          showSignButton={scheme.__config__.showSignButton}
          showAcceptButton={scheme.__config__.showAcceptButton}
          showRejectButton={scheme.__config__.showRejectButton}
          showCancelButton={scheme.__config__.showCancelButton}
          isMerge={scheme.__config__.isMerge}
          addDate={scheme.__config__.addDate}
          readonly={scheme.readonly}
          required={scheme.__config__.required}
          placeholder={scheme.placeholder}
          fieldName={scheme.__config__.__vModel__}
          defaultValue={scheme.__config__.defaultValue}
        ></sz-quick-reply>
      </div>
    );
  },
  tdFormItem(h, scheme, model) {
    let child = renderChildren.apply(this, arguments, model);
    const colspan = scheme.__config__.colspan;
    const rowspan = scheme.__config__.rowspan;
    return (
      <td
        style={scheme.__config__.style}
        colspan={colspan}
        rowspan={rowspan}
        class={scheme.__config__.jsClass}
      >
        {child}
      </td>
    );
  }
};

function renderFrom(h) {
  const { formConfCopy } = this;
  return (
    <el-row gutter={formConfCopy.gutter}>
      <el-form
        size={formConfCopy.size}
        label-position={formConfCopy.labelPosition}
        disabled={formConfCopy.disabled}
        label-width={`${formConfCopy.labelWidth}px`}
        ref={formConfCopy.formRef}
        // model不能直接赋值 https://github.com/vuejs/jsx/issues/49#issuecomment-472013664
        props={{ model: this[formConfCopy.formModel] }}
        rules={this[formConfCopy.formRules]}
      >
        {renderFormItem.call(
          this,
          h,
          formConfCopy.fields,
          formConfCopy.formModel
        )}
      </el-form>
    </el-row>
  );
}

function renderFormItem(h, elementList, model) {
  if (elementList === undefined) {
    return;
  }
  return elementList.map(scheme => {
    if (scheme) {
      const config = scheme.__config__;
      const layout = layouts[config.layout];

      if (layout) {
        if (model) {
          return layout.call(this, h, scheme, model);
        } else {
          return layout.call(this, h, scheme);
        }
      }
      throw new Error(`没有与${config.layout}匹配的layout`);
    }
  });
}

function renderChildren(h, scheme, model) {
  const config = scheme.__config__;
  if (!Array.isArray(config.children)) return null;
  return renderFormItem.call(this, h, config.children, model);
}

function setValue(event, config, scheme) {
  this.$set(config, "defaultValue", event);
  if (this[this.formConf.formModel]) {
    this.$set(
      this[this.formConf.formModel],
      scheme.__vModel__,
      event === undefined ? "" : event
    );
  }
}

function buildListeners(scheme) {
  const config = scheme.__config__;
  const methods = (this.formConf && this.formConf.__methods__) || {};
  const listeners = {};

  // 给__methods__中的方法绑定this和event
  Object.keys(methods).forEach(key => {
    listeners[key] = event => methods[key].call(this, event);
  });
  // 响应 render.js 中的 vModel $emit('input', val)
  listeners.input = event => setValue.call(this, event, config, scheme);

  return listeners;
}
import schema from "async-validator";
import { mapGetters } from "vuex";
import _ from "lodash";
export default {
  components: {
    render
  },
  props: {
    value: Object,
    formConf: {
      type: Object,
      required: true
    },
    pValid: {
      type: Boolean,
      default: false
    }
  },
  data() {
    const data = {
      formConfCopy: deepClone(this.formConf),
      [this.formConf.formModel]: {},
      [this.formConf.formRules]: {},
      formValid: false,
      that: this,
      vRules: []
    };
    this.initFormData(data.formConfCopy.fields, data[this.formConf.formModel]);
    this.buildRules(data.formConfCopy.fields, data[this.formConf.formRules]);
    return data;
  },
  computed: {
    ...mapGetters({
      mergeFormData: "mergeFormData",
      formItemChildren: "formItemChildren"
    })
  },
  methods: {
    // 动态表格更新数据
    updateFormChildren(val) {
      if (this.formConfCopy.fields) {
        this.updateSub(val, this.formConfCopy.fields);
        // console.log(this.formConfCopy.fields);
        this.initFormData(this.formConfCopy.fields, this[val.formModel]);
        this.buildRules(this.formConfCopy.fields, this[val.formRules]);
      }
    },
    updateSub(val, children) {
      if (children) {
        children.map(item => {
          if (item.__config__.formId === val.formId) {
            item.__config__.children = val.children;
          } else {
            if (item.__config__.children) {
              this.updateSub(val, item.__config__.children);
            }
          }
        });
      }
    },
    getLabelInput(val) {
      // console.log(val, "12221");
    },
    initFormData(componentList, formData) {
      if (componentList) {
        componentList.forEach(cur => {
          const config = cur.__config__;
          if (cur.__vModel__) {
            formData[cur.__vModel__] =
              config.defaultValue === undefined ? "" : config.defaultValue;
          }
          // else {
          //   if (config.__vModel__){
          //     formData[config.__vModel__] = (config.defaultValue===undefined?"":config.defaultValue)
          //   }
          //   else if (config.items){
          //     config.items.map(item=>{
          //       formData[item.__vModel__] = (item.defaultValue===undefined?"":item.defaultValue)
          //     })
          //   }
          // }
          if (config.children) this.initFormData(config.children, formData);
        });
      }
    },
    buildRules(componentList, rules) {
      if (componentList) {
        componentList.forEach(cur => {
          const config = cur.__config__;
          if (Array.isArray(config.regList)) {
            if (!cur.readonly && config.required) {
              const required = {
                required: config.required,
                message: cur.placeholder
              };
              if (config.tag == "el-select") {
                required.validator = selectVerification;
              }
              if (
                Array.isArray(config.defaultValue) ||
                config.tag === "sz-upload" ||
                config.tag === "sz-quick-reply" ||
                config.tag === "tag-input" ||
                config.layout === "tagInput"
              ) {
                required.type = "array";
                // required.message = `请至少选择一个${config.label}`
                required.message = cur.placeholder;
              } else if (config.tag === "sz-user-select") {
                required.type = "object";
                required.message = cur.placeholder;
              }
              required.message === undefined &&
                (required.message = `${config.label}不能为空`);
              config.regList.push(required);
            }
            let ruleTem = config.regList.map(item => {
              item.pattern && (item.pattern = eval(item.pattern));
              item.trigger = ruleTrigger && ruleTrigger[config.tag];
              return item;
            });
            if (cur.__vModel__) {
              this.$set(rules, [cur.__vModel__], ruleTem);
            } else if (config.__vModel__) {
              this.$set(rules, [config.__vModel__], ruleTem);
            }
          } else {
            if (config.items) {
              config.items.forEach(item => {
                if (Array.isArray(item.regList)) {
                  if (item.required) {
                    const required = {
                      required: item.required,
                      message: item.placeholder
                    };

                    required.message === undefined &&
                      (required.message = `${item.placeholder}`);
                    item.regList.push(required);
                  }
                  let ruleTemItem = item.regList.map(item => {
                    item.pattern && (item.pattern = eval(item.pattern));
                    item.trigger = ruleTrigger && ruleTrigger[item.tag];
                    return item;
                  });
                  this.$set(rules, [item.__vModel__], ruleTemItem);
                }
              });
            }
          }
          if (config.children) this.buildRules(config.children, rules);
        });
      }
    },

    /**
     * @deprecated 弃用
     * */
    resetForm() {
      this.formConfCopy = deepClone(this.formConf);
      this.$refs[this.formConf.formRef].resetFields();
    },

    /**
     * @deprecated 弃用
     * */
    submitForm() {
      if (this.$refs[this.formConf.formRef] !== undefined) {
        this.$refs[this.formConf.formRef]
          .validate(valid => {
            if (!valid) return false;
            // 触发sumit事件
            this.$emit("submit", this[this.formConf.formModel]);
            return true;
          })
          .finally(({ errors, fields }) => {
            // console.log(errors, fields);
          });
      }
    },

    /**
     * 验证表单，返回验证结果
     * */
    validateFormData() {
      this.$store.dispatch(
        "mergeFormDataAction",
        this[this.formConf.formModel]
      );
      this.vRules = _.cloneDeep(this[this.formConf.formRules]);
      this.setRules(this.formConfCopy.fields);
      let validator = new schema(this.vRules);
      // console.log(this.vRules)
      validator.validate(this.mergeFormData, (errors, fields) => {
        if (errors) {
          if (errors) {
            this.warningNotify("提示", errors[0].message);
          }
          this.formValid = false;
          this.$emit("update:pValid", this.formValid);
          // return handleErrors(errors, fields);
          return;
        }
        this.formValid = true;
        this.$emit("update:pValid", this.formValid);
      });
    },
    /**
     * 描述：递归设置检验条件
     * 时间： 2021/1/13
     */
    setRules(fields) {
      if (fields) {
        fields.map(item => {
          if (item.__config__.regBefore) {
            /**
             * 描述： 解析前置条件，不合符条件需要去除已经给定的检验规则，符合条件不处理。达到的效果即是：符合条件，进行检验。例子：title=6&count>1&title=*   （等于*表示有值就可以）
             * 时间： 2021/1/13
             */
            let temArr = item.__config__.regBefore.split("&");
            let result = true; // 都符合
            if (Array.isArray(temArr)) {
              temArr.map(reg => {
                if (!this.regCompare(reg)) {
                  result = false;
                }
              });
            } else {
              if (!this.regCompare(temArr)) {
                result = false;
              }
            }
            if (!result) {
              delete this.vRules[item.__config__.__vModel__];
            }
          }
          if (item.children) {
            this.setRules(item.children);
          }
        });
      }
    },

    /**
     * 描述：单个条件拆解
     * 时间： 2021/1/13
     */
    regCompare(reg) {
      // console.log(reg);
      let tem = reg.split("=");
      if (tem && tem.length === 2) {
        if (tem[1] === "*") {
          if (this.mergeFormData[tem[0]]) {
            return true;
          } else {
            return false;
          }
        } else {
          if (this.mergeFormData[tem[0]] === tem[1]) {
            return true;
          } else {
            return false;
          }
        }
      }
      tem = reg.split(">");
      if (tem && tem.length === 2) {
        if (this.mergeFormData[tem[0]] > tem[1]) {
          return true;
        } else {
          return false;
        }
      }
      tem = reg.split("<");
      if (tem && tem.length === 2) {
        if (this.mergeFormData[tem[0]] < tem[1]) {
          return true;
        } else {
          return false;
        }
      }
    },

    funAddSzFormItem(formId) {
      // console.log(formId);
    }
  },
  watch: {
    formConf(val) {
      // console.log(val,'有变动')
      if (val) {
        this.formConfCopy = {};
        this.formConfCopy = deepClone(val);
        this[val.formModel] = {};
        this[val.formRules] = {};
        this.initFormData(this.formConfCopy.fields, this[val.formModel]);
        this.buildRules(this.formConfCopy.fields, this[val.formRules]);
      }
    },
    formItemChildren: {
      handler(val) {
        this.updateFormChildren(val);
      },
      deep: true
    },
    // 回传数据到父组件
    formConfCopy: {
      handler(val) {
        if (val) {
          this.$emit("input", deepClone(val));
        }
      },
      deep: true
    },
    pValid: {
      immediate: true,
      handler(val) {
        this.formValid = val;
      }
    }
  },
  render(h) {
    return renderFrom.call(this, h);
  }
};
</script>
<style lang="less">
.test-form {
  .el-input__inner {
    border: none !important;
    border-radius: 0px;
    border-bottom: 1px solid #cececd !important;
  }
  .el-textarea__inner {
    border: none !important;
    border-radius: 0px;
    border-bottom: 1px solid #cececd !important;
  }
  .el-form-item__error {
    display: none;
  }
}
.table_form {
  .el-form-item {
    margin-bottom: 0px !important;
    border-radius: 0px;
  }
  .el-input__inner {
    border: none !important;
  }
  .el-textarea__inner {
    border: none !important;
    border-radius: 0px;
  }
  .el-form-item__error {
    display: none;
  }
}

.buttons_footer {
  margin-top: 10px;
}
</style>
